package com.letu.aopcache.manager;

import com.alibaba.fastjson.JSON;
import com.letu.aopcache.MethodCacheKeyBuilder;
import com.letu.aopcache.exception.CacheCenterConnectionException;
import com.letu.aopcache.model.CacheKeyTO;
import com.letu.aopcache.model.CacheWrapper;
import com.letu.aopcache.util.MSetParam;
import com.letu.aopcache.util.StringSerializer;
import com.letu.cache.repository.CacheOps;
import com.letu.cache.repository.CaffeineCacheOps;
import com.letu.core.model.CacheKey;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.time.Duration;
import java.util.*;

/**
 *
 */
@Getter
@Slf4j
public class AbstractRedisCacheManager implements ICacheManager {

    public static final StringSerializer KEY_SERIALIZER = new StringSerializer();

    /**
     * Hash的缓存时长：等于0时永久缓存；大于0时，主要是为了防止一些已经不用的缓存占用内存;hashExpire小于0时，则使用@Cache中设置的expire值（默认值为-1）。
     */
    protected int hashExpire = -1;

    protected CacheOps cacheOps;

    protected CaffeineCacheOps caffeineCacheOps;
    public AbstractRedisCacheManager(CacheOps cacheOps,CaffeineCacheOps caffeineCacheOps) {
        this.cacheOps = cacheOps;
        this.caffeineCacheOps = caffeineCacheOps;

    }

    @Override
    public void setCache(final CacheKeyTO cacheKeyTO, final CacheWrapper<Object> result, final Method method) throws CacheCenterConnectionException {
        if (null == cacheKeyTO) {
            return;
        }
        String cacheKey = cacheKeyTO.getCacheKey();
        if (null == cacheKey || cacheKey.isEmpty()) {
            return;
        }
        String hfield = cacheKeyTO.getHfield();
        int expire = result.getExpire();
        if (null == hfield || hfield.isEmpty()) {
            CacheKey key = new MethodCacheKeyBuilder().key(cacheKey);
            if (expire == NEVER_EXPIRE) {
                // TODO: 2022/7/2 设置过期时间
//            key.setExpire();
                cacheOps.set(key, result);
            } else if (expire > 0) {
                // TODO: 2022/7/2 设置过期时间
//            key.setExpire();
                cacheOps.set(key, result);
            }
        } else {
            CacheKey key = new MethodCacheKeyBuilder().key(hfield);
            byte[] field = KEY_SERIALIZER.serialize(hfield);
            int hExpire = hashExpire < 0 ? result.getExpire() : hashExpire;
            if (hExpire == NEVER_EXPIRE) {
                cacheOps.set(key, result);
            } else if (hExpire > 0) {
                cacheOps.set(key, result);
            }
        }
    }

    @Override
    public void setCacheOfCaffeine(final CacheKeyTO cacheKeyTO, final CacheWrapper<Object> result, final Method method) throws CacheCenterConnectionException {
        if (null == cacheKeyTO) {
            return;
        }
        String cacheKey = cacheKeyTO.getCacheKey();
        if (null == cacheKey || cacheKey.isEmpty()) {
            return;
        }
        String hfield = cacheKeyTO.getHfield();
        int expire = result.getExpire();
        if (null == hfield || hfield.isEmpty()) {
            CacheKey key = new MethodCacheKeyBuilder().key(cacheKey);
            if (expire == NEVER_EXPIRE) {
                // TODO: 2022/7/2 设置过期时间
//            key.setExpire();
                caffeineCacheOps.set(key, result);
            } else if (expire > 0) {
                // TODO: 2022/7/2 设置过期时间
//            key.setExpire();
                caffeineCacheOps.set(key, result);
            }
        } else {
            CacheKey key = new MethodCacheKeyBuilder().key(hfield);
            byte[] field = KEY_SERIALIZER.serialize(hfield);
            int hExpire = hashExpire < 0 ? result.getExpire() : hashExpire;
            if (hExpire == NEVER_EXPIRE) {
                caffeineCacheOps.set(key, result);
            } else if (hExpire > 0) {
                caffeineCacheOps.set(key, result);
            }
        }
    }
    @Override
    public void mset(final Method method, final Collection<MSetParam> params) {
        if (null == params || params.isEmpty()) {
            return;
        }
        CacheKeyTO cacheKeyTO;
        String cacheKey;
        String hfield;
        CacheWrapper<Object> result;
        for (MSetParam param : params) {
            if (null == param) {
                continue;
            }
            cacheKeyTO = param.getCacheKey();
            cacheKey = cacheKeyTO.getCacheKey();
            if (null == cacheKey || cacheKey.isEmpty()) {
                continue;
            }
            result = param.getResult();
            hfield = cacheKeyTO.getHfield();
            if (null == hfield || hfield.isEmpty()) {
                int expire = result.getExpire();
                if (expire == AbstractRedisCacheManager.NEVER_EXPIRE) {
                    CacheKey key = new MethodCacheKeyBuilder().key(cacheKey);
                    //减轻数据
                    result.setReturnType(null);
                    result.setHeader(null);
                    result.setArgs(null);
                    result.setObjectList(null);

                    result.setClassName(null);
                    result.setMethodName(null);
                    result.setInterfaceKey(null);
                    cacheOps.set(key, result);
                } else if (expire > 0) {
                    CacheKey key = new MethodCacheKeyBuilder().key(cacheKey);
                    key.setExpire(Duration.ofSeconds(expire));
                    //减轻数据
                    result.setReturnType(null);
                    result.setHeader(null);
                    result.setArgs(null);
                    result.setObjectList(null);

                    result.setClassName(null);
                    result.setMethodName(null);
                    result.setInterfaceKey(null);
                    cacheOps.set(key, result);
                }
            }
        }
    }

    @Override
    public void msetOfCaffeine(final Method method, final Collection<MSetParam> params) {
        if (null == params || params.isEmpty()) {
            return;
        }
        CacheKeyTO cacheKeyTO;
        String cacheKey;
        String hfield;
        CacheWrapper<Object> result;
        for (MSetParam param : params) {
            if (null == param) {
                continue;
            }
            cacheKeyTO = param.getCacheKey();
            cacheKey = cacheKeyTO.getCacheKey();
            if (null == cacheKey || cacheKey.isEmpty()) {
                continue;
            }
            result = param.getResult();
            hfield = cacheKeyTO.getHfield();
            if (null == hfield || hfield.isEmpty()) {
                int expire = result.getExpire();
                if (expire == AbstractRedisCacheManager.NEVER_EXPIRE) {
                    CacheKey key = new MethodCacheKeyBuilder().key(cacheKey);
                    //减轻数据
                    result.setReturnType(null);
                    result.setHeader(null);
                    result.setArgs(null);
                    result.setObjectList(null);

                    result.setClassName(null);
                    result.setMethodName(null);
                    result.setInterfaceKey(null);
                    caffeineCacheOps.set(key, result);
                } else if (expire > 0) {
                    CacheKey key = new MethodCacheKeyBuilder().key(cacheKey);
                    key.setExpire(Duration.ofSeconds(expire));
                    //减轻数据
                    result.setReturnType(null);
                    result.setHeader(null);
                    result.setArgs(null);
                    result.setObjectList(null);

                    result.setClassName(null);
                    result.setMethodName(null);
                    result.setInterfaceKey(null);
                    caffeineCacheOps.set(key, result);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public CacheWrapper<Object> get(final CacheKeyTO cacheKeyTO, final Method method) throws CacheCenterConnectionException {
        if (null == cacheKeyTO) {
            return null;
        }
        String cacheKey = cacheKeyTO.getCacheKey();
        if (null == cacheKey || cacheKey.isEmpty()) {
            return null;
        }
        CacheWrapper<Object> res = null;
        CacheKey key = new MethodCacheKeyBuilder().key(cacheKey);
        res = cacheOps.get(key);
        return res;
    }

    @Override
    public CacheWrapper<Object> getOfCaffeine(final CacheKeyTO cacheKeyTO, final Method method) throws CacheCenterConnectionException {
        if (null == cacheKeyTO) {
            return null;
        }
        String cacheKey = cacheKeyTO.getCacheKey();
        if (null == cacheKey || cacheKey.isEmpty()) {
            return null;
        }
        CacheWrapper<Object> res = null;
        CacheKey key = new MethodCacheKeyBuilder().key(cacheKey);
        res = caffeineCacheOps.get(key);
        return res;
    }

    @Override
    public Map<CacheKeyTO, CacheWrapper<Object>> mget(final Method method, final Type returnType, final Set<CacheKeyTO> keys) {
        if (null == keys || keys.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<CacheKeyTO, CacheWrapper<Object>> result = new HashMap<>();
        String hfield;
        String cacheKey;
        byte[] key;
        List<CacheKey> cacheKeys = new ArrayList<>();
        for (CacheKeyTO cacheKeyTO : keys) {
//            result.put(cacheKeyTO,null);
            cacheKey = cacheKeyTO.getCacheKey();
            if (null == cacheKey || cacheKey.isEmpty()) {
                continue;
            }
            cacheKeys.add(new MethodCacheKeyBuilder().key(cacheKey));
        }
        List<CacheWrapper<Object>> list = cacheOps.mGetByCacheKey(cacheKeys);

        int i = 0;
        for (CacheKeyTO cacheKeyTO : keys) {
            if (list.get(i) != null) {
                result.put(cacheKeyTO, list.get(i));
            }
            i++;
        }
//        log.info(JSON.toJSONString(result));


//            for (CacheKeyTO cacheKeyTO : keys) {
//                cacheKey = cacheKeyTO.getCacheKey();
//                if (null == cacheKey || cacheKey.isEmpty()) {
//                    continue;
//                }
//                hfield = cacheKeyTO.getHfield();
//                key = AbstractRedisCacheManager.KEY_SERIALIZER.serialize(cacheKey);
//                if (null == hfield || hfield.isEmpty()) {
//                    redisConnection.stringCommands().get(key);
//                } else {
//                    redisConnection.hashCommands().hGet(key, AbstractRedisCacheManager.KEY_SERIALIZER.serialize(hfield));
//                }
//            }
//        } finally {
//            return cacheManager.deserialize(keys, redisConnection.closePipeline(), returnType);
//        }
//
//
//        try (IRedis redis = getRedis()) {
//            cacheOps.sU
//            return redis.mget(returnType, keys);
//        } catch (Exception ex) {
//            log.error(ex.getMessage(), ex);
//        }
        return result;
    }

    public Map<CacheKeyTO, CacheWrapper<Object>> deserialize(Set<CacheKeyTO> keys, Collection<Object> values, Type returnType) throws Exception {
//        if (null == values || values.isEmpty()) {
//            return Collections.emptyMap();
//        }
//        CacheWrapper<Object> tmp;
//        Map<CacheKeyTO, CacheWrapper<Object>> res = new HashMap<>(keys.size());
//        Iterator<CacheKeyTO> keysIt = keys.iterator();
//        for (Object value : values) {
//            CacheKeyTO cacheKeyTO = keysIt.next();
//            if (null == value) {
//                continue;
//            }
//            if (!(value instanceof byte[])) {
//                log.warn("the data from redis is not byte[] but " + value.getClass().getName());
//                continue;
//            }
//            tmp = (CacheWrapper<Object>) serializer.deserialize((byte[]) value, returnType);
//            if (null != tmp) {
//                res.put(cacheKeyTO, tmp);
//            }
//        }
//        return res;
        return null;
    }

    @Override
    public void delete(Set<CacheKeyTO> keys) throws CacheCenterConnectionException {
//        if (null == keys || keys.isEmpty()) {
//            return;
//        }
//        try (IRedis redis = getRedis()) {
//            redis.delete(keys);
//        } catch (Exception ex) {
//            log.error(ex.getMessage(), ex);
//        }
    }

    public int getHashExpire() {
        return hashExpire;
    }

    public void setHashExpire(int hashExpire) {
        if (hashExpire < 0) {
            return;
        }
        this.hashExpire = hashExpire;
    }
}
